/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	"fs.h"
#include	<time.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>

static int mkfs(char *device, int swapsize, int inodenumber);

int
main(int argc, char *argv[])
{
	if(argc < 4){
		fprintf(stderr, "Usage: ./mkfs device swapsize inodenumber\n");
		exit(1);
	}
	return mkfs(argv[1], atoi(argv[2]), atoi(argv[3]));
}

static int
mkfs(char *device, int swapsize, int inodenumber)
{
	int fd;
	int mode;
	int buf[256];
	int i, j, n, m;
	struct inode ino;
	struct direct dir;
	struct filsys spb;
	struct stat stbuf;

	if(swapsize < 1024){
		fprintf(stderr, "Error: illegal swapsize %d: at least 1024\n", swapsize);
		return -1;
	}
	if(inodenumber < 100){ 
		fprintf(stderr, "Error: illegal inodenumber %d: at least 100\n", inodenumber);
		return -1;
	}
	if((fd = open(device, O_RDWR)) == -1){
		fprintf(stderr, "Error: cannot open device %s: %s\n", device, strerror(errno));
		return -1;
	}	
	if(fstat(fd, &stbuf) == -1){
		fprintf(stderr, "Error: cannot get status of device %s: %s\n", device, strerror(errno));
		return -1;
	}
	if(stbuf.st_size < 0x1400000){
		fprintf(stderr, "Error: device size at least 0x1400000\n");
		return -1;  
	}
	if(bread(fd, NRSVSCT, &spb, 1)){
		close(fd);
		fprintf(stderr, "Error: cannot read superblock of %s\n", device);
		return -1;
	}
	memset(&spb, 0, BLKSIZ);
	spb.s_magic = FS_MAGIC;
	spb.s_ssize = swapsize;
	spb.s_isize = inodenumber / NIOPB;
	spb.s_fsize = stbuf.st_size / BLKSIZ;
	for(i = NRSVSCT + SPBSIZ + spb.s_ssize, j = i + spb.s_isize; i < j; i++){
		if(bread(fd, i, buf, 1)){
			close(fd);
			fprintf(stderr, "Error: read fault\n");
			return -1;
		}
		memset(buf, 0, BLKSIZ);
		if(bwrite(fd, i, buf, 1)){
			close(fd);
			fprintf(stderr, "Error: write fault\n");
			return -1;	
		}
	}
	for(i = NRSVSCT + SPBSIZ + spb.s_ssize + spb.s_isize, j = spb.s_fsize; i < j; i += FS_BGRPSZ){
		if(bread(fd, i, buf, 1)){
			close(fd);
			fprintf(stderr, "Error: read fault\n");
			return -1;
		}
		for(n = 0; n < FS_BGRPSZ - 1; n++){
			m = 2 * n;
			buf[m + 1] = 0;
			buf[m] = i + 1 + n;
		}
		if(bwrite(fd, i, buf, 1)){
                        close(fd);
                        fprintf(stderr, "Error: write fault\n");
                        return -1;
                }
	}
	spb.s_time = time(NULL);
	spb.s_fsize = i > j ? i - FS_BGRPSZ : i;
	if(bwrite(fd, NRSVSCT, &spb, 1)){
		close(fd);
		fprintf(stderr, "Error: cannot update superblock of %s\n", device);
		return -1;
	}
	mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
	memset(&ino, 0, sizeof(ino));
	ino.i_nlink = 2;
	ino.i_mode = mode;
	ino.i_atime = ino.i_mtime = ino.i_ctime = time(NULL);
	if(iwrite(fd, &spb, ROOTINO, &ino)){
		close(fd);
		fprintf(stderr, "Error: cannot creat root directory\n");
		return -1;
	}
	dir.d_ino = ROOTINO;
	strcpy(dir.d_name, ".");
	if(wddir(fd, &spb, &ino, &dir, 0, ROOTINO)){
		close(fd);
		fprintf(stderr, "Error: cannot creat directory \".\"\n");
		return -1;
	}
	strcpy(dir.d_name, "..");
	if(wddir(fd, &spb, &ino, &dir, 0, ROOTINO)){
		close(fd);
		fprintf(stderr, "Error: cannot creat directory \"..\"\n");
		return -1;
	}
	return close(fd);
}

